#include "alp_dict.h"
#include "alp_list.h"
#include <string.h>
#include <stdlib.h>


#define ALP_DICT_COUNT 15

alp_status_t alp_dict_create(alp_dict_t **newdict)
{
	alp_dict_t* dict = (alp_dict_t*) malloc(sizeof(alp_dict_t));
	size_t i;
	*newdict = 0;
	if (dict == 0)
		return ALP_E_MEMORY;
	dict->count = ALP_DICT_COUNT;
	dict->buckets = (alp_list_t **) malloc(dict->count * sizeof(alp_list_t *));
	if (dict->buckets == 0)
		return ALP_E_MEMORY;
	for (i = 0; i < dict->count; ++i)
		if (alp_list_create(dict->buckets + i)<0)
			return ALP_E_MEMORY;
	*newdict = dict;
	return 0;
}

// Daniel Bernstein's magic 33
static unsigned hash33(const void *key, size_t *klen)
{
    unsigned r = 0;
    const unsigned char *p = (const unsigned char *)key;
    if (*klen == 0)
	{
		for (; *p; ++p)
			r = r * 33 + *p;
		*klen = p - (const unsigned char *)key;
	}
    else
	{
		size_t i = *klen;
        for (; i; --i, ++p)
            r = r * 33 + *p;
	}
    return r;
}

static entry_t * find_entry(alp_dict_t *dict, const void *key, size_t *klen, alp_list_t **plist)
{
	alp_list_t *list = dict->buckets[hash33(key, klen) % dict->count];
	alp_list_index_t *li = alp_list_first(list);
	entry_t *e;
	if (plist)
		*plist = list;
	for (; li; li = alp_list_next(li))
	{
		alp_list_this(li, (void **)&e);
		if (*klen == e->klen && memcmp(key, e->key, *klen) == 0)
			return e;
	}
	return 0;
}

void * alp_dict_get(alp_dict_t *dict, const void *key, size_t klen)
{
	entry_t *e = find_entry(dict, key, &klen, 0);
	if (e)
		return e->val;
	return 0;
}

alp_status_t alp_dict_set(alp_dict_t *dict, const void *key, size_t klen, void *val)
{
	alp_list_t *list;
	entry_t *e = find_entry(dict, key, &klen, &list);
	if (e)
	{
		e->val = val;
		return 0;
	}
	e = (entry_t *)malloc(sizeof(entry_t) + klen);
	if (e == 0)
		return ALP_E_MEMORY;
	e->key = (char *)e + sizeof(entry_t); //get the next e...
	memcpy(e->key, key, klen);
	e->klen = klen;
	e->val = val;
	return alp_list_append(list, e);
}

// check if di->li == 0
static alp_dict_index_t * check_di(alp_dict_index_t *di)
{
	for (; di->li == 0; )
	{
		++di->bucket_id;
		if (di->bucket_id == di->dict->count)
			return 0;
		di->li = alp_list_first(di->dict->buckets[di->bucket_id]);
	}
	return di;
}

alp_dict_index_t * alp_dict_first(alp_dict_t *dict)
{
	alp_dict_index_t *di = (alp_dict_index_t*)malloc(sizeof(alp_dict_index_t));
	if (di == 0)
		return 0;
	di->dict = dict;
	di->bucket_id = 0;
	di->li = alp_list_first(di->dict->buckets[0]);
	return check_di(di);
}

alp_dict_index_t * alp_dict_next(alp_dict_index_t *di)
{
	di->li = alp_list_next(di->li);
	return check_di(di);
}

void alp_dict_this(alp_dict_index_t *di, const void **key, size_t *klen, void **val)
{
	entry_t *e;
	alp_list_this(di->li, (void **)&e);
	if (key)
		*key = e->key;
	if (klen)
		*klen = e->klen;
	if (val)
		*val = e->val;
}



void alp_dict_destroy(alp_dict_t* dict)
{
	size_t i;
	for(i=0;i<dict->count;++i)
	{
		alp_list_destroy((dict->buckets[i]));
	}
	free(dict);
}
